from manimlib import *

class GraphExample(Scene):
  def construct(self):
    axes = Axes((-3, 10), (-1, 8))
    axes.add_coordinate_labels()
    self.play(Write(axes, lag_ratio = 0.01, run_time = 1))

    # Axes.get_graph会返回传入方程的图像
    sin_graph = axes.get_graph(
      lambda x: 2 * math.sin(x),
      color = BLUE
    )
    # 默认情况下，它在所有采样点(x, f(x))之间稍微平滑地插值
    # 但是，如果图形有棱角，可以将 use_smoothing 设为 False
    relu_graph = axes.get_graph(
      lambda x: max(x, 0),
      use_smoothing = False,
      color = YELLOW
    )

    # 对于不连续的函数，你可以指定间断点来让它不试图填补不连续的位置
    step_graph = axes.get_graph(
      lambda x: 2.0 if x >3 else 1.0,
      discontinuities= [3],
      color = GREEN
    )

    # Axes.get_graph_label可以接受字符串或者 mobject。如果传入的是字符串
    # 那么将将其当作 LaTeX 表达式传入Tex中
    # 默认下，label 将生成在图像的右侧，并且匹配图像的颜色
    sin_label = axes.get_graph_label(sin_graph, "\\sin(x)")
    relu_label = axes.get_graph_label(relu_graph, Text("ReLU"))
    step_label = axes.get_graph_label(step_graph, Text("Step"), x = 4)

    self.play(
      ShowCreation(sin_graph),
      FadeIn(sin_label, RIGHT)
    )
    self.wait(2)

    self.play(
      ReplacementTransform(sin_graph, relu_graph),
      FadeTransform(sin_label, relu_label)
    )
    self.wait()

    self.play(
      ReplacementTransform(relu_graph, step_graph),
      FadeTransform(relu_label, step_label)
    )
    self.wait()

    parabola = axes.get_graph(lambda x: 0.25 * x**2)
    parabola.set_stroke(BLUE)

    self.play(
        FadeOut(step_graph),
        FadeOut(step_label),
        ShowCreation(parabola)
    )
    self.wait()

    # 你可以使用 Axes.input_to_graph_point（缩写Axes.i2gp ）来找到图像上的一个点
    dot = Dot(color=RED)
    dot.move_to(axes.i2gp(2, parabola))
    self.play(FadeIn(dot, scale=0.5))
    # ValueTracker 存储一个数值，可以帮助我们制作可变参数的动画
    # 通常使用 updater 或者 f_always 让其它 mobject 根据其中的数值来更新
    x_tracker = ValueTracker(2)
    f_always(
        dot.move_to,
        lambda: axes.i2gp(x_tracker.get_value(), parabola)
    )
    self.play(x_tracker.animate.set_value(4), run_time=3)
    self.play(x_tracker.animate.set_value(-2), run_time=3)
    self.wait()